import { useState } from "react";

interface State<D> {
  error: Error | null;
  data: D | null;
  stat: "idle" | "loading" | "error" | "success";
}
// 使得抛出异常为可选项
const defaultConfig = {
  throwOnError: false,
};
// 默认的状态
const defaultInitialState: State<null> = {
  stat: "idle",
  data: null,
  error: null,
};
// 用户可以传入状态
export const useAsync = <D>(
  initialState?: State<D>,
  initialConfig?: typeof defaultConfig
) => {
  const config = { ...defaultConfig, ...initialConfig };
  // console.log(defaultConfig, initialConfig, config);
  const [state, setState] = useState<State<D>>({
    ...defaultInitialState,
    // 用户传入的状态可以覆盖默认值
    ...initialState,
  });
  // 请求成功的时候调用setData
  const setData = (data: D) =>
    setState({
      data,
      stat: "success",
      error: null,
    });
  // 请求失败的时候调用setError
  const setError = (error: Error) =>
    setState({
      data: null,
      stat: "error",
      error,
    });
  const [retry, setRetry] = useState(() => () => {

  })
  // 用来触发异步请求
  const run = (promise: Promise<D>, runConfig?:{retry:()=>Promise<D>}) => {
    // 如果调用hook run的时候没有传入promise 抛出错误
    if (!promise || !promise.then) {
      throw new Error("请传入Promise类型的数据");
    }
    setState({
      ...state,
      stat: "loading",
    });
    setRetry(() => () => {
      if (runConfig?.retry) {
        run(runConfig?.retry(), runConfig)
      }
    })
    return (
      promise
        .then((data) => {
          setData(data);
          return data;
        })
        // FIXME catch会消化异常 如果不主动抛出 外面接收不到
        // 当同步和异步混合的时候 要用try catch否则会出现错误
        .catch((err) => {
          setError(err);
          // console.log(config.throwOnError);
          if (config.throwOnError) {
            // console.log("进来");
            return Promise.reject(err);
          }
          return err;
        })
    );
  };
  // 返回一个状态
  return {
    isIdle: state.stat === "idle",
    isLoading: state.stat === "loading",
    isSuccess: state.stat === "success",
    isError: state.stat === "error",
    run,
    setData,
    setError,
    // retry被调用的时候重新跑一遍run，让state刷新
    retry,
    ...state,
  };
};
